package com.example.user.configuration;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
//import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

/**
 * sentinel规则：
 */
@Configuration
public class SentinelFlowRuleConfig {

    /***
     * 初始化规则
     */
    @PostConstruct
    private void initFlowRule() {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        //为 SentinelController 资源定义流控规则
        FlowRule rule = new FlowRule("SentinelController");
        // 流控阈值
        rule.setCount(2);
        //设置限流阈值类型，0=并发模式，默认1 qps模式
        rule.setGrade(1);
        //设置流控效果：默认直接拒绝/warmup/queue
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        //将定义的规则添加到集合中
        rules.add(rule);
        //加载规则
        FlowRuleManager.loadRules(rules);
    }

    @PostConstruct
    public void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource("SentinelController");
        /**
         * 设置熔断策略
         * DEGRADE_GRADE_RT:平均响应时间[慢调用]
         * DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
         * DEGRADE_GRADE_EXCEPTION_COUNT:异常数
         */
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        //设置阈值
        rule.setCount(2);
        //设置 熔断时长
        rule.setTimeWindow(30);
        // 统计时长（单位为 ms） 默认1000
        rule.setStatIntervalMs(60*1000);
        //将规则添加到集合中
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }


    /***
     * 系统自我保护
     */
    @PostConstruct
    private void initSystemRule() {
        List<SystemRule> rules = new ArrayList<>();
        //创建系统自我保护规则
        SystemRule rule = new SystemRule();
        //CPU使用率 值为[0,1],-1 (不生效)
        rule.setHighestCpuUsage(0.5);
        //所有入口资源的 QPS,-1 (不生效)
        rule.setQps(100);
        //入口流量的最大并发数,-1 (不生效)
        rule.setMaxThread(100);
        //所有入口流量的平均响应时间,单位：毫秒,-1 (不生效)
        rule.setAvgRt(1000);
        //load1 触发值，用于触发自适应控制阶段,系统最高负载，建议取值 CPU cores * 2.5
        rule.setHighestSystemLoad(20);
        //将规则加入到集合
        rules.add(rule);
        SystemRuleManager.loadRules(rules);
    }


    @PostConstruct
    public void initAuthorityRule() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("SentinelController");
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        rule.setLimitApp("TaiKuLa,appB,ordersevice");
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }

    /**
     * Sentinel提供了 RequestOriginParser 接口来处理访问来源，Sentinel保护的资源如果被访问，
     * 就会调用 RequestOriginParser解析访问来源
     */
    @Component
    public class MyRequestOriginParser implements RequestOriginParser {

        /**
         * 约定基于请求头 Origin 携带请求方来源标识
         * @param request
         * @return
         */
        @Override
        public String parseOrigin(HttpServletRequest request) {
            String origin = request.getHeader("Origin");
            return origin == null ? "FORBID": origin;
        }
    }



    /***
     * 热点参数限流配置
     */
    //  @PostConstruct
    private static void initParamFlowRules() {
        ParamFlowRule rule = new ParamFlowRule("searchDriver")
                //目标资源参与流控的参数下标
                .setParamIdx(0)
                //限流模式为QPS
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                //统计窗口时间长度（单位为秒）
                .setDurationInSec(10)
                //流控效果（支持快速失败和匀速排队模式）
                //CONTROL_BEHAVIOR_DEFAULT：限流行为，直接拒绝
                //CONTROL_BEHAVIOR_WARM_UP:限流行为，匀速排队
                //CONTROL_BEHAVIOR_RATE_LIMITER：限流行为，匀速排队
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
                //最大排队等待时长（仅在匀速排队模式生效  CONTROL_BEHAVIOR_RATE_LIMITER）
                //.setMaxQueueingTimeMs(600)
                //最大阈值为5
                .setCount(5);

        // 针对指定的参数值单独设置限流阈值，不受前面 count 阈值的限制

        //如下配置：当参数值为恩施的时候，阈值到达2的时候则执行限流就开始限流,不用等到前面设置的阈值5
        ParamFlowItem item = new ParamFlowItem()
                //参数类型为String类型
                .setClassType(String.class.getName())
                //设置阈值为2
                .setCount(2)
                //需要统计的值
                .setObject(String.valueOf("恩施"));

        rule.setParamFlowItemList(Collections.singletonList(item));

        //加载热点数据
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }




    // @PostConstruct
//    public  void loadMyNamespaceRules() {
//        Properties properties = new Properties();
//        properties.put(PropertyKeyConst.SERVER_ADDR, "192.168.10.30:8848");// server addr
//        properties.put(PropertyKeyConst.NAMESPACE, "486db17a-2f34-42c0-99ce-7d8fe8ccf516");// namespaceid
//
//        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(
//                properties,
//                "SENTINEL_GROUP",//group
//                "driver-flow-rules",//dataId
//                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}) //Converter
//        );
//        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
//    }



}
